import util
import blue
import math
import cameras
import mathUtil
CAMERA_ZOOM_CLOSE = 0
CAMERA_ZOOM_FAR = 1
MAX_SPIN_SPEED = 15
MAX_MOUSE_DELTA = 0.2
SPIN_RESISTANCE = 0.99
SPIN_DELTA_MODIFIER = 60
ZOOM_SPEED_MODIFIER = 2.0
SPIN_FALLOFF_RATIO = 700.0

class IncarnaCamera(cameras.WorldspaceCamera):
    __guid__ = 'cameras.IncarnaCamera'

    def __init__(self):
        cameras.WorldspaceCamera.__init__(self)
        self.zoom = 1.0
        self.desiredZoom = self.zoom
        self.collisionZoom = self.zoom
        self.collisionCorrectionPerFrame = 0
        self.minZoom = 1.0
        self.maxZoom = 1.8
        self.minPitch = 0.885
        self.maxPitch = 2.5
        self.wantToToggleZoom = False
        self.zoomAccelerate = 1.0
        self.mouseYawDelta = 0
        self.mousePitchDelta = 0
        self.zoomToggle = CAMERA_ZOOM_CLOSE
        self.yawSpeed = 0
        self.pitchSpeed = 0
        self.lastMouseMoveDelta = (0, 0)



    def OnMouseUp(self, button):
        if (button is const.INPUT_TYPE_LEFTCLICK):
            self.mouseLeftButtonDown = False
        elif (button is const.INPUT_TYPE_RIGHTCLICK):
            self.mouseRightButtonDown = False
        self.yawSpeed = min(cameras.MAX_SPIN_SPEED, (self.lastMouseMoveDelta[0] * cameras.SPIN_DELTA_MODIFIER))
        self.pitchSpeed = min(cameras.MAX_SPIN_SPEED, (self.lastMouseMoveDelta[1] * cameras.SPIN_DELTA_MODIFIER))



    def OnMouseMove(self, deltaX, deltaY):
        if (self.mouseLeftButtonDown or self.mouseRightButtonDown):
            self.AdjustYaw(deltaX)
            self.AdjustPitch(deltaY)
            self.lastMouseMoveDelta = (deltaX, deltaY)



    def AdjustYaw(self, delta, maxRotate = None, ignoreUpdate = True):
        if (delta < 0):
            delta = max(delta, -cameras.MAX_MOUSE_DELTA)
        else:
            delta = min(delta, cameras.MAX_MOUSE_DELTA)
        self.mouseYawDelta = delta
        if maxRotate:
            if (delta > maxRotate):
                delta = maxRotate
            if (delta < -maxRotate):
                delta = -maxRotate
        self.SetYaw((self.yaw + delta), ignoreUpdate=ignoreUpdate)



    def AdjustPitch(self, delta):
        if (delta < 0):
            delta = max(delta, -cameras.MAX_MOUSE_DELTA)
        else:
            delta = min(delta, cameras.MAX_MOUSE_DELTA)
        self.mousePitchDelta = delta
        self.SetPitch((self.pitch + delta))



    def AdjustZoom(self, delta):
        if (((self.zoomToggle == CAMERA_ZOOM_CLOSE) and (delta > 0)) or ((self.zoomToggle == CAMERA_ZOOM_FAR) and (delta < 0))):
            self.wantToToggleZoom = True



    def SetZoom(self, zoom):
        self.desiredZoom = zoom
        self.desiredZoom = max(self.desiredZoom, self.minZoom)
        self.desiredZoom = min(self.desiredZoom, self.maxZoom)



    def SetCollisionZoom(self, zoom, amountPerFrame):
        self.collisionZoom = zoom
        self.collisionCorrectionPerFrame = amountPerFrame



    def ClipLowSpinSpeed(self, value):
        if (abs(value) < 0.001):
            return 0.0
        else:
            return value



    def Update(self):
        now = blue.os.GetTime()
        frameTime = (float((now - self.lastUpdateTime)) / const.SEC)
        if ((not self.mouseLeftButtonDown) and (not self.mouseRightButtonDown)):
            if (self.yawSpeed != 0):
                self.SetYaw((self.yaw + (self.yawSpeed * frameTime)))
                self.yawSpeed = (self.yawSpeed * pow(cameras.SPIN_RESISTANCE, (frameTime * cameras.SPIN_FALLOFF_RATIO)))
                self.yawSpeed = self.ClipLowSpinSpeed(self.yawSpeed)
            if (self.pitchSpeed != 0):
                self.SetPitch((self.pitch + (self.pitchSpeed * frameTime)))
                self.pitchSpeed = (self.pitchSpeed * pow(cameras.SPIN_RESISTANCE, (frameTime * cameras.SPIN_FALLOFF_RATIO)))
                self.pitchSpeed = self.ClipLowSpinSpeed(self.pitchSpeed)
        if (abs((self.zoom - self.collisionZoom)) > 0.01):
            if (abs((self.zoom - self.collisionZoom)) <= abs(self.collisionCorrectionPerFrame)):
                self.zoom = self.collisionZoom
                self.zoomAccelerate = 1.0
            elif ((self.zoom + abs((self.collisionCorrectionPerFrame * self.zoomAccelerate))) < self.collisionZoom):
                self.zoom += abs((self.collisionCorrectionPerFrame * self.zoomAccelerate))
            elif ((self.zoom - abs((self.collisionCorrectionPerFrame * self.zoomAccelerate))) > self.collisionZoom):
                self.zoom -= abs((self.collisionCorrectionPerFrame * self.zoomAccelerate))
            else:
                self.zoom = self.collisionZoom
            self.desiredZoom = self.zoom
        else:
            self.zoom = mathUtil.Lerp(self.zoom, self.desiredZoom, ((frameTime * cameras.ZOOM_SPEED_MODIFIER) * self.zoomAccelerate))
            self.collisionZoom = self.zoom
            if (self.zoomAccelerate > 1.0):
                self.zoomAccelerate = (self.zoomAccelerate ** 0.9)
                if (self.zoomAccelerate < 1.0):
                    self.zoomAccelerate = 1.0
        self.lastUpdateTime = now
        for (priority, behavior,) in self.cameraBehaviors:
            behavior.ProcessCameraUpdate(self, now, frameTime)

        self.mouseYawDelta = 0
        self.mousePitchDelta = 0
        self.AssembleViewMatrix(self.poi, self.yaw, self.pitch, self.zoom)
        self.lastMouseMoveDelta = (0, 0)



exports = util.AutoExports('cameras', locals())

